﻿using PetaPoco;
using PmSoft.Core;
using PmSoft.Core.Domain.Entities;
using PmSoft.Core.EventBus;
using PmSoft.Core.Extensions;
using PmSoft.Data.Abstractions;
using PmSoft.Data.Abstractions.Attributes;
using PmSoft.Data.Abstractions.Events;
using System.Diagnostics;
using System.Reflection;

namespace PmSoft.Data.PetaPoco.Repositories;

/// <summary>
/// 基于 PetaPoco 的实体仓储基类，提供基本的 CRUD 操作
/// </summary>
/// <typeparam name="TEntity">实体类型，必须实现 IEntity<TKey></typeparam>
/// <typeparam name="TKey">主键类型，不可为空</typeparam>
public abstract partial class Repository<TEntity, TKey>
	where TEntity : class, IEntity<TKey>, new()
	where TKey : notnull
{
	/// <summary>
	/// 构造函数，初始化仓储上下文
	/// </summary>
	/// <param name="dbContext">PetaPoco 数据库上下文</param>
	/// <param name="applicationContext">应用程序上下文</param>
	protected Repository(PetaPocoDbContext dbContext, IApplicationContext applicationContext)
	{
		DbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
		ApplicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext));
	}

	/// <summary>
	/// 应用程序上下文
	/// </summary>
	protected IApplicationContext ApplicationContext { get; }

	/// <summary>
	/// PetaPoco 数据库上下文
	/// </summary>
	protected PetaPocoDbContext DbContext { get; }

	/// <summary>
	/// 事件总线，用于发布实体操作事件
	/// </summary>
	protected IEventBus EventBus => ApplicationContext.GetRequiredService<IEventBus>();

	#region Insert

	/// <summary>
	/// 插入实体（同步）
	/// </summary>
	/// <param name="entity">要插入的实体</param>
	/// <returns>插入后的主键值</returns>
	public virtual object Insert(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		object id = DbContext.Insert(entity);
		var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
		if (auditableAttribute != null)
			EventBus.Publish(new EntityAddedEventArgs(ApplicationContext, entity, auditableAttribute));
		return id;
	}

	/// <summary>
	/// 插入实体（异步）
	/// </summary>
	/// <param name="entity">要插入的实体</param>
	/// <returns>插入后的主键值</returns>
	public virtual async Task<object> InsertAsync(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		object id = await DbContext.InsertAsync(entity);
		var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
		if (auditableAttribute != null)
			await EventBus.PublishAsync(new EntityAddedEventArgs(ApplicationContext, entity, auditableAttribute));
		return id;
	}

	#endregion

	#region Update

	/// <summary>
	/// 更新实体（同步）
	/// </summary>
	/// <param name="entity">要更新的实体</param>
	/// <returns>受影响的行数</returns>
	public virtual int Update(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		TEntity? oldEntity = DbContext.SingleOrDefault<TEntity>(entity.Id);
		if (oldEntity == null) throw new ArgumentException($"未找到 ID 为 {entity.Id} 的实体", nameof(entity));
		int rowsAffected = DbContext.Update(entity);
		if (rowsAffected > 0)
		{
			var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
			if (auditableAttribute != null)
			{
				if (entity is IDelEntity<TKey> delEntity && delEntity.IsDeleted
				&& oldEntity is IDelEntity<TKey> delOldEntity && !delOldEntity.IsDeleted)
					EventBus.Publish(new EntityDeletedEventArgs(ApplicationContext, entity, auditableAttribute));
				else
					EventBus.Publish(new EntityUpdatedEventArgs(ApplicationContext, oldEntity, entity, auditableAttribute));
			}
		}
		return rowsAffected;
	}

	/// <summary>
	/// 更新实体（异步）
	/// </summary>
	/// <param name="entity">要更新的实体</param>
	/// <returns>受影响的行数</returns>
	public virtual async Task<int> UpdateAsync(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		TEntity? oldEntity = await DbContext.SingleOrDefaultAsync<TEntity>(entity.Id);
		if (oldEntity == null) throw new ArgumentException($"未找到 ID 为 {entity.Id} 的实体", nameof(entity));
		int rowsAffected = await DbContext.UpdateAsync(entity);
		if (rowsAffected > 0)
		{
			var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
			if (auditableAttribute != null)
			{
				if (entity is IDelEntity<TKey> delEntity && delEntity.IsDeleted
				&& oldEntity is IDelEntity<TKey> delOldEntity && !delOldEntity.IsDeleted)
					await EventBus.PublishAsync(new EntityDeletedEventArgs(ApplicationContext, entity, auditableAttribute));
				else
					await EventBus.PublishAsync(new EntityUpdatedEventArgs(ApplicationContext, oldEntity, entity, auditableAttribute));
			}
		}
		return rowsAffected;
	}

	#endregion

	#region BulkSync
	/// <summary>
	/// 批量同步数据，自动执行插入、更新和删除操作。
	/// </summary>
	/// <param name="dbEntities">数据库现有数据集合（外部查询并传入）</param>
	/// <param name="newEntities">新的数据集合（需要同步的数据）</param>
	/// <param name="keySelector">用于匹配数据的唯一键选择器（如 Id）</param>
	/// <param name="updateAction">自定义更新逻辑（可为空，不传则仅执行插入和删除）</param>
	/// <returns>异步任务</returns>
	public virtual async Task BulkSyncAsync(
		IEnumerable<TEntity> dbEntities,
		IEnumerable<TEntity> newEntities,
		Func<TEntity, object> keySelector,
		Action<TEntity, TEntity>? updateAction = null) // 
	{
		// 1️ 计算交集（需要更新的项）
		// 交集是 dbEntities 和 newEntities 中存在相同 keySelector 值的项
		var commonEntities = dbEntities.IntersectByKeys(newEntities, keySelector).ToList();

		// 2️ 计算差集（需要删除的项）
		// 计算出 dbEntities 中有但 newEntities 中没有的项，这些需要删除
		var toDelete = dbEntities.ExceptByKeys(newEntities, keySelector).ToList();

		// 3️ 计算差集（需要插入的项）
		// 计算出 newEntities 中有但 dbEntities 中没有的项，这些需要插入
		var toInsert = newEntities.ExceptByKeys(dbEntities, keySelector).ToList();

		// 4️ 处理更新（如果 updateAction 不为空，则遍历交集并应用更新逻辑）
		if (updateAction != null)
		{
			foreach (var existingEntity in commonEntities)
			{
				// 找到新数据中对应的实体
				var newEntity = newEntities.First(e => keySelector(e).Equals(keySelector(existingEntity)));

				// 调用外部传入的 updateAction 方法，执行更新
				updateAction(existingEntity, newEntity);
			}
		}

		// 5️ 处理删除（删除数据库中存在但新数据中不存在的项）
		foreach (var entity in toDelete)
		{
			await DeleteAsync(entity);
		}

		// 6️ 处理插入（插入新数据中存在但数据库中不存在的项）
		foreach (var entity in toInsert)
		{
			await InsertAsync(entity);
		}
	} 
	#endregion

	#region Delete

	/// <summary>
	/// 删除实体（同步）
	/// </summary>
	/// <param name="entity">要删除的实体</param>
	/// <returns>受影响的行数</returns>
	public virtual int Delete(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		int rowsAffected = DbContext.Delete(entity);
		var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
		if (auditableAttribute != null)
			EventBus.Publish(new EntityDeletedEventArgs(ApplicationContext, entity, auditableAttribute));
		return rowsAffected;
	}

	/// <summary>
	/// 根据实体 ID 删除（同步）
	/// </summary>
	/// <param name="entityId">实体 ID</param>
	/// <returns>受影响的行数，若实体不存在则返回 0</returns>
	public virtual int DeleteByEntityId(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		TEntity? entity = Get(entityId);
		return entity == null ? 0 : Delete(entity);
	}

	/// <summary>
	/// 删除实体（异步）
	/// </summary>
	/// <param name="entity">要删除的实体</param>
	/// <returns>受影响的行数</returns>
	public virtual async Task<int> DeleteAsync(TEntity entity)
	{
		if (entity == null) throw new ArgumentNullException(nameof(entity));
		int rowsAffected = await DbContext.DeleteAsync(entity);
		var auditableAttribute = EntityMetadataCache.GetMetadata(typeof(TEntity)).AuditableAttribute;
		if (auditableAttribute != null)
			await EventBus.PublishAsync(new EntityDeletedEventArgs(ApplicationContext, entity, auditableAttribute));
		return rowsAffected;
	}

	/// <summary>
	/// 根据实体 ID 删除（异步）
	/// </summary>
	/// <param name="entityId">实体 ID</param>
	/// <returns>受影响的行数，若实体不存在则返回 0</returns>
	public virtual async Task<int> DeleteByEntityIdAsync(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		TEntity? entity = await GetAsync(entityId);
		return entity == null ? 0 : await DeleteAsync(entity);
	}

	#endregion

	#region Exists

	/// <summary>
	/// 检查实体是否存在（同步）
	/// </summary>
	/// <param name="entityId">实体 ID</param>
	/// <returns>是否存在</returns>
	public bool Exists(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		return DbContext.Exists<TEntity>(entityId);
	}

	/// <summary>
	/// 检查实体是否存在（异步）
	/// </summary>
	/// <param name="entityId">实体 ID</param>
	/// <returns>是否存在</returns>
	public async Task<bool> ExistsAsync(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		return await DbContext.ExistsAsync<TEntity>(entityId);
	}

	#endregion

	#region Get First

	/// <summary>
	/// 获取单个实体（同步）
	/// </summary>
	/// <param name="entityId">实体 ID</param>
	/// <returns>实体对象，若不存在则返回 null</returns>
	public virtual TEntity? Get(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		return DbContext.SingleOrDefault<TEntity>(entityId);
	}

	/// <summary>
	/// 获取单个实体（异步）
	/// </summary>
	/// <param name="entityId">实体 ID</param>
	/// <returns>实体对象，若不存在则返回 null</returns>
	public virtual async Task<TEntity?> GetAsync(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		return await DbContext.SingleOrDefaultAsync<TEntity>(entityId);
	}

	#endregion

	#region Page

	/// <summary>
	/// 分页查询（同步）
	/// </summary>
	/// <typeparam name="TResult">结果类型</typeparam>
	/// <param name="sql">SQL 查询语句</param>
	/// <param name="pageIndex">页码（从 1 开始）</param>
	/// <param name="pageSize">每页大小</param>
	/// <returns>分页结果</returns>
	public virtual IPagedList<TResult> Paging<TResult>(Sql sql, int pageIndex, int pageSize)
	{
		if (sql == null) throw new ArgumentNullException(nameof(sql));
		if (pageIndex < 1) throw new ArgumentOutOfRangeException(nameof(pageIndex), "页码必须大于等于 1");
		if (pageSize < 1) throw new ArgumentOutOfRangeException(nameof(pageSize), "每页大小必须大于等于 1");

		var stopwatch = Stopwatch.StartNew();
		Page<TResult> page = DbContext.Page<TResult>(pageIndex, pageSize, sql);
		stopwatch.Stop();

		return new PagedList<TResult>(page.Items)
		{
			PageIndex = pageIndex,
			PageSize = pageSize,
			Total = (int)page.TotalItems,
			QueryDuration = stopwatch.ElapsedMilliseconds
		};
	}

	/// <summary>
	/// 分页查询（异步）
	/// </summary>
	/// <typeparam name="TResult">结果类型</typeparam>
	/// <param name="sql">SQL 查询语句</param>
	/// <param name="pageIndex">页码（从 1 开始）</param>
	/// <param name="pageSize">每页大小</param>
	/// <returns>分页结果</returns>
	public virtual async Task<IPagedList<TResult>> PagingAsync<TResult>(Sql sql, int pageIndex, int pageSize)
	{
		if (sql == null) throw new ArgumentNullException(nameof(sql));
		if (pageIndex < 1) throw new ArgumentOutOfRangeException(nameof(pageIndex), "页码必须大于等于 1");
		if (pageSize < 1) throw new ArgumentOutOfRangeException(nameof(pageSize), "每页大小必须大于等于 1");

		var stopwatch = Stopwatch.StartNew();
		Page<TResult> page = await DbContext.PageAsync<TResult>(pageIndex, pageSize, sql);
		stopwatch.Stop();

		return new PagedList<TResult>(page.Items)
		{
			PageIndex = pageIndex,
			PageSize = pageSize,
			Total = (int)page.TotalItems,
			QueryDuration = stopwatch.ElapsedMilliseconds
		};
	}

	#endregion
}